"""
-------------------------------
- File_Name: logs.py
- Date: 2025/2/12
- Author: yangzhide
- Email: xxzjyzd@126.com
- Description: 常用工具函数
-------------------------------
"""
import re
from typing import Dict, List


# 日志解析

def parse_log_line(line) -> Dict:
    """将单行日志解析为结构化字典"""

    parts = [part.strip() for part in line.split("|") if part.strip()]

    if len(parts) < 8:
        return None

    log_entry = {
        "timestamp": parts[0][0:19],
        "id": parts[1],
        "level": parts[2],
        "path": parts[3],
        "module": parts[4],
        "line_number": parts[5],
        "module_name": parts[6],
        "message": parts[7],
    }

    # 进一步解析消息内容（动态提取用户、操作对象）
    message = log_entry["message"]
    user_match = re.search(r"用户【(.*?)】", message)
    if user_match:
        log_entry["user"] = user_match.group(1)
    else:
        log_entry["user"] = "SYSTEM"

    # 提取操作对象（如学生名、资源ID）
    # obj_match = re.search(r"查询学生【(.*?)】", message)
    # if obj_match:
    #     log_entry["target"] = obj_match.group(1)
    # elif "查询资源" in message:
    #     res_match = re.search(r"查询资源【(.*?)】", message)
    #     if res_match:
    #         log_entry["resource"] = res_match.group(1)
    #
    # # 错误信息提取
    # if log_entry["level"] == "ERROR":
    #     error_match = re.search(r"异常: (\d+): (.*)", message)
    #     if error_match:
    #         log_entry["error_code"] = int(error_match.group(1))
    #         log_entry["error_detail"] = error_match.group(2)

    return log_entry


def read_log(file_path: str, page: int = 1, page_size: int = 10) -> List[Dict]:
    """分页读取日志文件解析"""
    logs = []
    with open(file_path, "r", encoding="utf-8") as f:
        # 计算偏移量（适合小文件）
        skip = (page - 1) * page_size
        for _ in range(skip):
            if not f.readline():
                break

        # 读取指定页数的日志
        count = 0
        while count < page_size:
            line = f.readline()
            if not line:
                break
            parsed = parse_log_line(line)
            if parsed:
                logs.append(parsed)
                count += 1
    return logs


import datetime
import re
import time

# from bson import ObjectId
import orjson

LAYOUT_PREFIX = 'layout.'
VIEW_PREFIX = 'view.'
FIRST_LEVEL_ROUTE_COMPONENT_SPLIT = '$'


def check_url(url: str = "/api/v1/system-manage/roles/{role_id}/buttons",
              url2: str = "/api/v1/system-manage/roles/1/buttons") -> bool:
    pattern = re.sub(r'\{.*?}', '[^/]+', url)
    if re.match(pattern, url2):
        return True
    return False


def get_layout_and_page(component=None):
    layout = ''
    page = ''

    if component:
        layout_or_page, page_item = component.split(FIRST_LEVEL_ROUTE_COMPONENT_SPLIT)
        layout = get_layout(layout_or_page)
        page = get_page(page_item or layout_or_page)

    return layout, page


def get_layout(layout):
    return layout.replace(LAYOUT_PREFIX, '') if layout.startswith(LAYOUT_PREFIX) else ''


def get_page(page):
    return page.replace(VIEW_PREFIX, '') if page.startswith(VIEW_PREFIX) else ''


def transform_layout_and_page_to_component(layout, page):
    if layout and page:
        return f"{LAYOUT_PREFIX}{layout}{FIRST_LEVEL_ROUTE_COMPONENT_SPLIT}{VIEW_PREFIX}{page}"
    elif layout:
        return f"{LAYOUT_PREFIX}{layout}"
    elif page:
        return f"{VIEW_PREFIX}{page}"
    else:
        return ''


def get_route_path_by_route_name(route_name):
    return f"/{route_name.replace('_', '/')}"


def get_path_param_from_route_path(route_path):
    path, param = route_path.split('/:')
    return path, param


def get_route_path_with_param(route_path, param):
    if param.strip():
        return f"{route_path}/:{param}"
    else:
        return route_path


def camel_case_convert(data: dict):
    """
    将字典的键从下划线命名法（snake case）转换为小驼峰命名法（lower camel case）
    例：{"user_name": 1} => {"userName": 1}
    :param data:
    :return:
    """
    converted_data = {}
    for key, value in data.items():
        converted_key = ''.join(word.capitalize() if i else word for i, word in enumerate(key.split('_')))
        converted_data[converted_key] = value
        # converted_data[to_snake_case(key)] = value
    return converted_data


def snake_case_convert(data: dict):
    """
    将字典的键从小驼峰命名法（lower camel case）或其他格式转换为下划线命名法（snake case)
    例：{"userName": 1} => {"user_name": 1}
    :param data:
    :return:
    """
    converted_data = {}
    for key, value in data.items():
        converted_data[to_snake_case(key)] = value
    return converted_data


def to_snake_case(x):
    """
    将字符串从驼峰命名法（包括小驼峰和大驼峰）转换为下划线命名法
    例：userLoginCount => user_login_count
    :param x:
    :return:
    """
    return re.sub(r'(?<=[a-z])[A-Z]|(?<!^)[A-Z](?=[a-z])', '_\\g<0>', x).lower()


def to_camel_case(x):
    """
    将字符串从下划线命名法转换为小驼峰命名法（首字母小写）
    例：user_login_count => userLoginCount
    :param x:
    :return:
    """
    return re.sub('_([a-zA-Z])', lambda m: (m.group(1).upper()), x)


def to_upper_camel_case(x):
    """
    将字符串从下划线命名法转换为大驼峰命名法（首字母大写）
    例：user_login_count => UserLoginCount
    :param x:
    :return:
    """
    s = re.sub('_([a-zA-Z])', lambda m: (m.group(1).upper()), x)
    return s[0].upper() + s[1:]


def to_lower_camel_case(x):
    """
    将字符串从下划线命名法转换为小驼峰命名法（首字母小写）
    :param x:
    :return:
    """
    s = re.sub('_([a-zA-Z])', lambda m: (m.group(1).upper()), x)
    return s[0].lower() + s[1:]

def convert_keys_to_camel_case(data):
    """递归将字典或列表中的所有键转换为小驼峰命名"""
    if isinstance(data, dict):
        # 如果是字典，转换键并递归处理值
        return { to_lower_camel_case(k): convert_keys_to_camel_case(v) for k, v in data.items()}
    elif isinstance(data, list):
        # 如果是列表，递归处理每个元素
        return [convert_keys_to_camel_case(item) for item in data]
    else:
        # 如果是其他类型，直接返回
        return data

# 这里可以处理一些原本处理不了的格式（ObjectId）或者自定义显示格式（datetime）
def _default(obj):
    if isinstance(obj, datetime.datetime):
        if obj != obj:
            return None
        if obj.hour == 0 and obj.minute == 0:
            return obj.strftime("%Y-%m-%d")
        return obj.strftime("%Y-%m-%d %H:%M:%S")
    elif isinstance(obj, datetime.date):
        return obj.isoformat()
    # elif isinstance(obj, ObjectId):
    #     return obj.__str__()
    elif hasattr(obj, "asdict"):
        return obj.asdict()
    elif hasattr(obj, "_asdict"):  # namedtuple
        return obj._asdict()
    elif hasattr(obj, '__dict__'):
        return obj.__dict__
    else:
        raise TypeError(f"Unsupported json dump type: {type(obj)}")


def orjson_dumps(data):
    # 这里的样式通过 | 的方式叠加， 其实每个对应的是一个数字， 更多的样式可以见上面的 github 链接
    option = orjson.OPT_PASSTHROUGH_DATETIME | orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_INDENT_2
    rv = orjson.dumps(data, default=_default, option=option)
    # rv = orjson.dumps(data, default=_default)
    return rv.decode(encoding='utf-8')


def timestamp_to_time(timestamp):
    time_struct = time.localtime(timestamp)
    time_string = time.strftime("%Y-%m-%d %H:%M:%S", time_struct)
    return time_string


def time_to_timestamp(dt="2023-06-01 00:00:00"):
    timeArray = time.strptime(dt, "%Y-%m-%d %H:%M:%S")
    timestamp = time.mktime(timeArray)
    return str(int(timestamp))


"""orm 查询工具
filter_fields：字典列表中，过滤出新的字典列表，只有需要的字段
extract_field_values：字典列表中，过滤出一个列表，只包含字段的值
"""


def filter_fields(
        data_list: List[dict],
        fields_to_keep: str | list[str]
) -> List[dict]:
    """
    保留指定字段的字典列表转换
    :param data_list: 原始数据列表
    :param fields_to_keep: 需要保留的字段（字符串或列表）
    :return: 过滤后的新列表
    """
    if isinstance(fields_to_keep, str):
        fields = [fields_to_keep]
    else:
        fields = fields_to_keep

    return [
        {key: item[key] for key in fields if key in item}
        for item in data_list
    ]


def extract_field_values(
        data_list: List[dict],
        field_name: str
) -> List:
    """
    提取指定字段值的纯列表
    :param data_list: 原始数据列表
    :param field_name: 需要提取的字段名
    :return: 字段值组成的列表
    """
    return [item[field_name] for item in data_list if field_name in item]

